﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Linq2Crm.Helper;
using Linq2Crm.Interpreter;
using Linq2Crm.ObjectModel;
using Linq2Crm.ObjectModel.Attributes;
using Linq2Crm.ObjectModel.DomainObject;
using Linq2Crm.Request;
using Linq2Crm.Request.SpecificRequest;
using SilverCrmSoap.CrmSdk;


namespace Linq2Crm
{
	public sealed partial class DomainObjectContext
	{
		internal void ExecuteRelationshipRequest(ManyToManyRelationshipRequest request)
		{
			IncrementCounter();
			if (!_manyToManyRelationshipMetadatas.ContainsKey(request.ManyToManyRelationshipName))
			{
				OrganizationRequest innerRequest = new OrganizationRequest();
				innerRequest.RequestName = "RetrieveRelationship";
				innerRequest.Parameters.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>(){Key = "Name", Value = request.ManyToManyRelationshipName});
				innerRequest.Parameters.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>() {Key = "MetadataId",Value = Guid.Empty});
				innerRequest.Parameters.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>(){Key = "RetrieveAsIfPublished",Value = false});

				OrganizationService.BeginExecute(innerRequest,
															ar =>
															{
																OrganizationResponse result = null;
																try
																{
																	result = OrganizationService.EndExecute(ar);
																}
																catch (Exception ex)
																{
																	request.Callback.Invoke(new ManyToManyRelationshipResponse
																										{
																											Error = ex,
																											Successful = false
																										});
																	DecrementCounter();
																	return;
																}

																ManyToManyRelationshipMetadata data;

																if (result.Results.Count == 0 && !(result.Results[0].Value is ManyToManyRelationshipMetadata))
																{
																	if (request.Callback != null)
																	{
																		request.Callback.Invoke(new ManyToManyRelationshipResponse
																											{
																												Error = new ArgumentException("Metadata doesn't belong to ManyToMany relationship"),
																												Successful = false
																											});
																	}
																	DecrementCounter();
																	return;
																}
																data = (ManyToManyRelationshipMetadata)result.Results[0].Value;
																if (!_manyToManyRelationshipMetadatas.ContainsKey(request.ManyToManyRelationshipName))
																{
																	_manyToManyRelationshipMetadatas.Add(request.ManyToManyRelationshipName, data);
																}

																GetRelationShip(request, data);
															}, request);
			}
			else
			{
				GetRelationShip(request, _manyToManyRelationshipMetadatas[request.ManyToManyRelationshipName]);
			}
		}

		private void GetRelationShip(ManyToManyRelationshipRequest request, ManyToManyRelationshipMetadata data)
		{
			QueryExpression query = new QueryExpression();
			query.Criteria = new FilterExpression();
									

			if (request.FEntityFilter != null && request.FEntityFilter.Any())
			{
				ConditionExpression condition = new ConditionExpression();
				condition.AttributeName = data.Entity1IntersectAttribute;
				condition.Operator = ConditionOperator.In;
				condition.Values = new ObservableCollection<object>(request.FEntityFilter.Cast<object>());
				query.Criteria.Conditions.Add(condition);
			}
			if (request.SEntityFilter != null && request.SEntityFilter.Any())
			{
				ConditionExpression condition = new ConditionExpression();
				condition.AttributeName = data.Entity2IntersectAttribute;
				condition.Operator = ConditionOperator.In;
				condition.Values = new ObservableCollection<object>(request.SEntityFilter.Cast<object>());
				query.Criteria.Conditions.Add(condition);
			}
			query.EntityName = data.IntersectEntityName;

			query.ColumnSet = new ColumnSet
										{
											AllColumns = true,
										};

			request.InnerMetadata = data;
			OrganizationService.BeginRetrieveMultiple(query, ExecuteRelationshipRequestCompleted, request);
		}

		private void ExecuteRelationshipRequestCompleted(IAsyncResult ar)
		{
			ManyToManyRelationshipRequest request = (ManyToManyRelationshipRequest)ar.AsyncState;
			ManyToManyRelationshipResponse response = new ManyToManyRelationshipResponse();
			try
			{
				EntityCollection collection = OrganizationService.EndRetrieveMultiple(ar);

				foreach (Entity entity in collection.Entities)
				{
					
					Guid key = entity.GetAttributeValue<Guid>(request.InnerMetadata.Entity1IntersectAttribute);
					if (!response.Results.ContainsKey(key))
					{
						response.Results.Add(key, new List<Guid>());
					}
					response.Results[key].Add(entity.GetAttributeValue<Guid>(request.InnerMetadata.Entity2IntersectAttribute));
				}
				response.Successful = true;
			}
			catch (Exception ex)
			{
				response.Error = ex;
				response.Successful = false;
			}
			if (request.Callback != null)
			{
				request.Callback.Invoke(response);
			}
			DecrementCounter();
		}

		internal void ExecuteOrganizationRequest(OrganizationRequestWrapper request)
		{
			IncrementCounter();
			OrganizationService.BeginExecute(request.OrganizationRequest, EndExecuteOrganizationRequestCompleted, request);
		}

		private void EndExecuteOrganizationRequestCompleted(IAsyncResult ar)
		{
			OrganizationRequestWrapper request = (OrganizationRequestWrapper)ar.AsyncState;
			OrganizationResponseWrapper response = new OrganizationResponseWrapper();
			try
			{
				OrganizationResponse resp = OrganizationService.EndExecute(ar);
				response.Results = resp.Results.ToDictionary(x => x.Key, z => z.Value);
				response.Successful = true;
			}
			catch (Exception ex)
			{
				response.Error = ex;
				response.Successful = false;
			}
			if (request.Callback != null)
			{
				request.Callback.Invoke(response);
			}
			DecrementCounter();
		}

		internal void ExecuteSettingsRequest(SettingsRequest request)
		{
			IncrementCounter();
			OrganizationRequest oRequest = new OrganizationRequest { RequestName = "RetrieveUserSettingsSystemUser" };
			oRequest.Parameters.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>(){ Key = "EntityId",Value = request.UserId});
			oRequest.Parameters.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>(){Key = "ColumnSet", Value = new ColumnSet() { AllColumns = true, Columns = new ObservableCollection<string>() { "" } }});
			OrganizationService.BeginExecute(oRequest, ExecuteSettingsRequestCompleted, request);
		}

		private void ExecuteSettingsRequestCompleted(IAsyncResult ar)
		{
			SettingsRequest request = (SettingsRequest)ar.AsyncState;
			SettingsResponse response = new SettingsResponse();
			try
			{
				OrganizationResponse orgResponse = OrganizationService.EndExecute(ar);
				SilverCrmSoap.CrmSdk.KeyValuePair<string, object> settingsresponse = orgResponse.Results.First();
				Entity rEntity = settingsresponse.Value as Entity;
				if (rEntity != null)
				{
					foreach (var attribute in rEntity.Attributes)
					{
						response.UserSettings.Add(attribute.Key, attribute.Value);
					}
					response.Successful = true;
				}
				else
				{
					throw new Exception("Cannot get user settings");
				}
			}
			catch (Exception e)
			{
				response.Error = e;
				response.Successful = false;
			}
			if (request.Callback != null)
			{
				request.Callback.Invoke(response);
			}
			DecrementCounter();
		}

		internal void ExecuteWhoAmIRequest(WhoAmIRequestWrapper request)
		{
			IncrementCounter();
			OrganizationService.BeginExecute(request.WhoAmIRequest, ExecuteWhoAmIRequestComleted, request);
		}

		private void ExecuteWhoAmIRequestComleted(IAsyncResult ar)
		{
			WhoAmIRequestWrapper request = (WhoAmIRequestWrapper)ar.AsyncState;
			WhoAmIResponseWrapper response = new WhoAmIResponseWrapper();
			try
			{
				OrganizationResponse resp = (OrganizationResponse)OrganizationService.EndExecute(ar);
				response.BusinessUnitId = (Guid)resp["BusinessUnitId"];
				response.OrganizationId = (Guid)resp["OrganizationId"];
				response.UserId = (Guid)resp["UserId"];
				SettingsRequest sr = SettingsRequest.Create(response.UserId);
				sr.Callback =
					settingsResponse =>
						{
							response.Successful = true;
							response.UserSettings = settingsResponse.UserSettings;
							if (!settingsResponse.Successful)
							{
								response.Error = settingsResponse.Error;
								response.Successful = false;
							}							
							if (request.Callback != null)
							{
								request.Callback.Invoke(response);
							}
							DecrementCounter();
						};
				ExecuteSettingsRequest(sr);				
			}
			catch (Exception ex)
			{
				response.Error = ex;
				response.Successful = false;

				if (request.Callback != null)
				{
					request.Callback.Invoke(response);
				}
				DecrementCounter();
			}
		}

		internal void ExecuteGetRequest(GetRequest request)
		{
			IncrementCounter();

			ExpressionInterpreter interpreter = new ExpressionInterpreter();
			ExpressionResult interpretedResult = interpreter.LinQToQueryExpression(request.Query.Expression);

			QueryExpression multiQuery = interpretedResult.QueryExpression;

			//check if there's values in "Contain" and if nothing then return nothing since there wouldn't be any record to match non-existing filter
			if (!IsProperFilter(multiQuery.Criteria))
			{
				GetResponse gr = new GetResponse
				{
					Successful = true,
					Items = new List<IDomainObject>()
				};
				if (request.Callback != null)
				{
					request.Callback.Invoke(gr);
				}
				DecrementCounter();
				return;
			}

			var entities = new List<string>()
			               	{
			               		multiQuery.EntityName
			               	};

			entities.AddRange(GetLinkedEntitiesNames(multiQuery.LinkEntities));

			ExecuteWithAttributeRequest((ex) =>
																						{
																							if (ex != null)
																							{
																								GetResponse gr = new GetResponse
																														{
																															Error = ex,
																															Successful = false
																														};
																								if (request.Callback != null)
																								{
																									request.Callback.Invoke(gr);
																								}
																								DecrementCounter();
																								return;
																							}
																							foreach (string column in multiQuery.ColumnSet.Columns)
																							{
                                                                        if (_attributesList[multiQuery.EntityName].Attributes.Any(x => x.LogicalName.Equals(column)))
																								{
																									continue;
																								}
																								GetResponse gr = new GetResponse();
																								gr.Error = new ArgumentOutOfRangeException("column", string.Format("Entity {1} doesn't contain column with name {0}", column, multiQuery.EntityName));
																								gr.Successful = false;
																								request.Callback(gr);
																								DecrementCounter();
																								return;
																							}
																							OrganizationService.BeginRetrieveMultiple(multiQuery, ExecuteGetRequestCompleted, request);
																						}, entities.ToArray());
		}

		private IEnumerable<string> GetLinkedEntitiesNames(IEnumerable<LinkEntity> linkEntities)
		{
			var result = new List<string>();
			if (linkEntities == null || !linkEntities.Any())
			{
				return result;
			}
			foreach (LinkEntity linkEntity in linkEntities)
			{
				if (!String.IsNullOrEmpty(linkEntity.EntityAlias))
				{
					result.Add(linkEntity.EntityAlias);
				}
				if (linkEntity.LinkEntities != null && !linkEntity.LinkEntities.Any())
				{
					result.AddRange(GetLinkedEntitiesNames(linkEntity.LinkEntities));
				}
			}
			return result;
		}

		private bool IsProperFilter(FilterExpression criteria)
		{
			if (criteria == null)
			{
				return true;
			}
			foreach (ConditionExpression conditionExpression in criteria.Conditions)
			{
				if (conditionExpression.Operator == ConditionOperator.In && !conditionExpression.Values.Any())
				{
					return false;
				}
			}

			if (criteria.Filters == null || !criteria.Filters.Any())
			{
				return true;
			}
			foreach (FilterExpression filterExpression in criteria.Filters)
			{
				if (!IsProperFilter(filterExpression))
					return false;
			}
			return true;
		}

		private void RetrieveAttributeList(string entity, Action<Exception> postAction)
		{
			IncrementCounter();
			OrganizationRequest request = new OrganizationRequest
														{
															RequestName = "RetrieveEntity"
														};
			request.Parameters.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>(){ Key = "EntityFilters", Value = EntityFilters.Attributes});
			request.Parameters.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>(){ Key = "LogicalName",Value =  entity});
			request.Parameters.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>(){ Key = "MetadataId",Value =  Guid.Empty});
			request.Parameters.Add(new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>(){ Key = "RetrieveAsIfPublished",Value =  false});
			OrganizationService.BeginExecute(request, ar =>
																		{
																			try
																			{
																				OrganizationResponse result = OrganizationService.EndExecute(ar);
																				if (result.Results.Any())
																				{
																					if (!_attributesList.ContainsKey(entity))
																					{
																						_attributesList.Add(entity, ((EntityMetadata)result.Results.First().Value));
																					}
																				}

																				postAction(null);
																			}
																			catch (Exception e)
																			{
																				postAction(e);
																			}
																			DecrementCounter();
																		}, null);
		}

		internal void ExecuteDeleteRequest(DeleteRequest request)
		{
			IncrementCounter();

			if (request.Entity.OrigStatusCode == null)
			{
				DeleteResponse dr = new DeleteResponse
											{
												Successful = true
											};

				if (request.Callback != null)
				{
					request.Callback.Invoke(dr);
				}
				DecrementCounter();
				return;
			}

			AttributeParser ap = new AttributeParser(request.DomainObjectType);
			AnalyzeResult analyze = ap.AnalyzeType();

			switch (request.DeleteMethod)
			{
				case DeleteRequest.DeleteType.ChangeStatus:

					ExecuteWithAttributeRequest((ex) =>
																						{
																							if (ex != null)
																							{
																								var deleteResponse = new DeleteResponse
																								{
																									Error = ex,
																									Successful = false
																								};
																								if (request.Callback != null)
																								{
																									request.Callback.Invoke(deleteResponse);
																								}
																								DecrementCounter();
																								return;
																							}
                                                                     DomainObjectFactory domF = new DomainObjectFactory(_attributesList[analyze.EntityName].Attributes);
																							Dictionary<RecordStateCode, int> statuses = domF.GetDefaultStatuses();
																							SetStatusRequest ss = new SetStatusRequest(request.DomainObjectType, RecordStateCode.Inactive, statuses[RecordStateCode.Inactive]);
																							ss.Entity = request.Entity;

																							ss.Callback = (response) =>
																												{
																													var deleteResponse = new DeleteResponse
																																					{
																																						Error = response.Error,
																																						Successful = response.Successful
																																					};
																													if (request.Callback != null)
																													{
																														request.Callback.Invoke(deleteResponse);
																													}
																													DecrementCounter();
																												};
																							ExecuteSetStatusRequest(ss);
																						}, analyze.EntityName);
					break;
				case DeleteRequest.DeleteType.Delete:
					OrganizationService.BeginDelete(analyze.EntityName, request.Entity.Id, ExecuteDeleteRequestCompleted, request);
					break;
				default:
					throw new ArgumentOutOfRangeException();
			}
		}

		internal void ExecuteSetStatusRequest(SetStatusRequest request)
		{
			IncrementCounter();
			AttributeParser ap = new AttributeParser(request.DomainObjectType);
			AnalyzeResult analyze = ap.AnalyzeType();

         DomainObjectFactory domF = new DomainObjectFactory(_attributesList[analyze.EntityName].Attributes);
			Dictionary<RecordStateCode, Dictionary<int, string>> list = domF.GetStatusList();

			bool needStateRequest = false;

			if (request.Entity.StatusCode != -1 && request.Entity.OrigStatusCode != request.StatusCode && list.Any(x => x.Value.Any(z => z.Key == request.StatusCode)))
			{
				request.StateCode = (int)list.FirstOrDefault(x => x.Value.ContainsKey(request.StatusCode)).Key;
				needStateRequest = true;
			}
			else //if stateCode has changed but status was not
			{
				if (request.Entity.StateCode == RecordStateCode.Active && !list[RecordStateCode.Active].Any(x => x.Key == request.Entity.OrigStatusCode))
				{
					request.StateCode = (int)RecordStateCode.Active;
					request.StatusCode = -1;
					needStateRequest = true;
				}
				else if (request.Entity.StateCode == RecordStateCode.Inactive && !list[RecordStateCode.Inactive].Any(x => x.Key == request.Entity.OrigStatusCode))
				{
					request.StateCode = (int)RecordStateCode.Inactive;
					request.StatusCode = -1;
					needStateRequest = true;
				}
			}

			//if statuscode is not exist
			if (request.StatusCode != -1 && !list.Any(x => x.Value.Any(z => z.Key == request.StatusCode)))
			{
				SetStatusResponse response =
					new SetStatusResponse
						{
							Successful = false,
							Error = new ArgumentOutOfRangeException("StatusCode", string.Format("StatusOptionSet doesn't contain StatusCode ({0}) for specified State({1})", request.StatusCode, request.StateCode))
						};
				if (request.Callback != null)
				{
					request.Callback.Invoke(response);
				}
				DecrementCounter();
				return;
			}

			if (needStateRequest)
			{

				OrganizationRequest sRequest = new OrganizationRequest
				{
					RequestName = "SetState",
					Parameters = new ParameterCollection()
				};
				sRequest["EntityMoniker"] = new EntityReference
				{
					LogicalName = analyze.EntityName,
					Id = request.Entity.Id
				};
				sRequest["State"] = new OptionSetValue
				{
					Value = request.StateCode
				};
				sRequest["Status"] = new OptionSetValue
				{
					Value = request.StatusCode
				};

				OrganizationService.BeginExecute(sRequest, ExecuteSetStatusRequestCompleted, request);

			}
			else
			{
				SetStatusResponse response = new SetStatusResponse
														{
															Successful = true
														};
				if (request.Callback != null)
				{
					request.Callback.Invoke(response);
				}
				DecrementCounter();
			}
		}

		internal void ExecuteUpdateRequest(UpdateRequest request)
		{
			IncrementCounter();
			if (request.Entity.OrigStatusCode == null)
			{
				#region Insert with state change
				InsertRequest ir = new InsertRequest(request.DomainObjectType, request.Entity);

				SetStatusRequest sStatusRequest = new SetStatusRequest(request.DomainObjectType, request.Entity.StateCode, request.Entity.StatusCode)
																{
																	Entity = request.Entity,
																	Callback = ssResponse =>
																					{
																						UpdateResponse ur = new UpdateResponse
																													{
																														Error = ssResponse.Error,
																														Successful = ssResponse.Successful
																													};
																						if (request.Callback != null)
																						{
																							request.Callback.Invoke(ur);
																						}
																						DecrementCounter();
																					}
																};

				ir.Callback = (iResponse) =>
									{
										if (!iResponse.Successful)
										{
											UpdateResponse updateResponse = new UpdateResponse
																						{
																							Error = iResponse.Error,
																							Successful = iResponse.Successful
																						};
											if (request.Callback != null)
											{
												request.Callback.Invoke(updateResponse);
											}
											DecrementCounter();
										}
										else
										{
											ExecuteSetStatusRequest(sStatusRequest);
										}
									};
				ExecuteInsertRequest(ir);
				#endregion

				return;
			}
			//First - Update, then - change state
			AttributeParser ap = new AttributeParser(request.DomainObjectType);
			AnalyzeResult analyzeResults = ap.AnalyzeType();
			ExecuteWithAttributeRequest((ex) =>
			{
				if (ex != null)
				{
					UpdateResponse ur = new UpdateResponse
					{
						Error = ex,
						Successful = false
					};
					if (request.Callback != null)
					{
						request.Callback.Invoke(ur);
					}
					DecrementCounter();
					return;
				}

				SetStatusRequest setStatusRequest = new SetStatusRequest(request.DomainObjectType, request.Entity.StateCode, request.Entity.StatusCode);
				setStatusRequest.Entity = request.Entity;
				setStatusRequest.Callback =
					(ssResponse) =>
					{
						UpdateResponse ur = new UpdateResponse
													{
														Error = ssResponse.Error,
														Successful = ssResponse.Successful
													};

						if (request.Callback != null)
						{
							request.Callback.Invoke(ur);
						}
						DecrementCounter();
					};

            DomainObjectFactory domF = new DomainObjectFactory(_attributesList[analyzeResults.EntityName].Attributes);
				Entity entity = domF.CreateCrmEntityFrom(request.Entity, CheckType.Updating);
				OrganizationService.BeginUpdate(entity,
					ar =>
					{
						UpdateResponse response = new UpdateResponse();
						try
						{
							OrganizationService.EndUpdate(ar);
							response.Successful = true;
						}
						catch (Exception e)
						{
							response.Error = e;
							response.Successful = false;
						}

						if (response.Successful)
						{
							ExecuteSetStatusRequest(setStatusRequest);
						}
						else
						{
							if (request.Callback != null)
							{
								request.Callback.Invoke(response);
							}
							DecrementCounter();
						}
					}, request);
			}, analyzeResults.EntityName);
		}

		internal void ExecuteInsertRequest(InsertRequest request)
		{
			IncrementCounter();
			AttributeParser ap = new AttributeParser(request.DomainObjectType);
			AnalyzeResult analyzeResults = ap.AnalyzeType();
			ExecuteWithAttributeRequest((ex) =>
																						{
																							if (ex != null)
																							{
																								InsertResponse response = new InsertResponse();
																								response.Successful = false;
																								response.Error = ex;
																								if (request.Callback != null)
																								{
																									request.Callback.Invoke(response);
																								}
																								DecrementCounter();
																								return;
																							}

                                                                     DomainObjectFactory domF = new DomainObjectFactory(_attributesList[analyzeResults.EntityName].Attributes);
																							Entity entity = domF.CreateCrmEntityFrom(request.Entity, CheckType.Inserting);

																							OrganizationService.BeginCreate(entity, ExecuteInsertRequestCompleted, request);
																						}, analyzeResults.EntityName);
		}

		private void ExecuteInsertRequestCompleted(IAsyncResult ar)
		{
			InsertResponse response = new InsertResponse();
			InsertRequest request = (InsertRequest)ar.AsyncState;

			AttributeParser ap = new AttributeParser(request.DomainObjectType);
			AnalyzeResult analyzeResults = ap.AnalyzeType();
         DomainObjectFactory domF = new DomainObjectFactory(_attributesList[analyzeResults.EntityName].Attributes);

			try
			{
				Guid newId = OrganizationService.EndCreate(ar);
				request.Entity.Id = newId;
				response.Id = newId;
				request.Entity.StateCode = (int)RecordStateCode.Active;
				request.Entity.OrigStatusCode = request.Entity.StatusCode = domF.GetDefaultStatuses()[RecordStateCode.Active];

				response.Successful = true;
			}
			catch (Exception ex)
			{
				response.Successful = false;
				response.Error = ex;
			}

			if (request.Callback != null)
			{
				request.Callback.Invoke(response);
			}
			DecrementCounter();
		}

		private void ExecuteWithAttributeRequest(Action<Exception> action, params string[] entityNames)
		{
			if (entityNames == null || !entityNames.Any())
			{
				action(null);
			}
			else
			{
				var entName = entityNames.First();
				if (!_attributesList.ContainsKey(entName))
				{
					RetrieveAttributeList(entName, ex =>
																	{
																		if (ex != null)
																		{
																			action(ex);
																		}
																		else
																		{
																			ExecuteWithAttributeRequest(action, entityNames.Skip(1).ToArray());
																		}
																	});
				}
				else
				{
					ExecuteWithAttributeRequest(action, entityNames.Skip(1).ToArray());
				}
			}
		}

		private void ExecuteSetStatusRequestCompleted(IAsyncResult ar)
		{
			SetStatusResponse response = new SetStatusResponse();
			SetStatusRequest request = (SetStatusRequest)ar.AsyncState;
			try
			{
				OrganizationResponse ssResponse = (OrganizationResponse)OrganizationService.EndExecute(ar);

				request.Entity.StatusCode = request.StatusCode;
				request.Entity.OrigStatusCode = request.StatusCode;

				response.Successful = true;
			}
			catch (Exception ex)
			{
				response.Error = ex;
				response.Successful = false;
			}
			if (request.Callback != null)
			{
				request.Callback.Invoke(response);
			}
			DecrementCounter();
		}

		private void ExecuteDeleteRequestCompleted(IAsyncResult ar)
		{
			DeleteResponse response = new DeleteResponse();
			DeleteRequest request = (DeleteRequest)ar.AsyncState;
			try
			{
				OrganizationService.EndDelete(ar);
				response.Successful = true;
			}
			catch (Exception ex)
			{
				response.Error = ex;
				response.Successful = false;
			}
			if (request.Callback != null)
			{
				request.Callback.Invoke(response);
			}
			DecrementCounter();
		}

		private void ExecuteGetRequestCompleted(IAsyncResult ar)
		{
			GetResponse response = new GetResponse();
			GetRequest request = (GetRequest)ar.AsyncState;
			try
			{
				EntityCollection collection = OrganizationService.EndRetrieveMultiple(ar);

            DomainObjectFactory domF = new DomainObjectFactory(_attributesList[collection.EntityName].Attributes);
				foreach (Entity entity in collection.Entities)
				{
					IDomainObject item = domF.CreateDomainObjectFrom(entity, request.DomainObjectType);

					FillLinkedEntities(item, entity);

					response.Items.Add(item);
				}
				response.Successful = true;
			}
			catch (Exception ex)
			{
				response.Error = ex;
				response.Successful = false;
				Debug.Assert(false);
			}
			if (request.Callback != null)
			{
				request.Callback.Invoke(response);
			}
			DecrementCounter();
		}

		private void FillLinkedEntities(IDomainObject item, Entity entity)
		{
			if (!entity.Attributes.Any(x => x.Value is AliasedValue))
			{
				return;
			}
			foreach (PropertyInfo propertyInfo in item.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).Where(x => x.PropertyType.GetInterfaces().Contains(typeof(IDomainObject))))
			{
				var attrib = propertyInfo.PropertyType.GetCustomAttributes(typeof(CrmEntityMappingAttribute), true).FirstOrDefault();
				if (attrib != null)
				{
					var schemaName = ((CrmEntityMappingAttribute)attrib).EntitySchemaName;
					if (!_attributesList.ContainsKey(schemaName))
					{
						continue;
					}

               DomainObjectFactory domF = new DomainObjectFactory(_attributesList[schemaName].Attributes);
					var entityToParse = new Entity()
												{
													Attributes = new AttributeCollection(),
													LogicalName = schemaName
												};
					entityToParse.Attributes.AddRange(entity.Attributes.Where(x => x.Value is AliasedValue && x.Key.StartsWith(schemaName)).Select(x => new SilverCrmSoap.CrmSdk.KeyValuePair<string, object>(){Key = ((AliasedValue)x.Value).AttributeLogicalName,Value = ((AliasedValue)x.Value).Value}));
					var nestedType = domF.CreateDomainObjectFrom(entityToParse, propertyInfo.PropertyType);

					propertyInfo.SetValue(item, nestedType, null);
				}
			}
		}
	}
}
